////////////////////////////////////////////////////////////////////////////////
// This file is part of the Mist compiler.                                    //
//                                                                            //
// Copyright (C) 2009 The Mist project                                        //
//                                                                            //
// For a list of contributors, see the COMMITTERS file.                       //
//                                                                            //
// The Mist compiler is free software: you can redistribute it and/or modify  //
// it under the terms of the GNU General Public License as published by       //
// the Free Software Foundation, either version 3 of the License, or          //
// (at your option) any later version.                                        //
//                                                                            //
// The Mist compiler is distributed in the hope that it will be useful,       //
// but WITHOUT ANY WARRANTY; without even the implied warranty of             //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              //
// GNU General Public License for more details.                               //
//                                                                            //
// You should have received a copy of the GNU General Public License          //
// in the COPYING file. If not, see <http://www.gnu.org/licenses/>.           //
////////////////////////////////////////////////////////////////////////////////

///
/// \file
/// Contains the classes describing an operator in Mist.
///

#ifndef OPERATOR_HPP
#define OPERATOR_HPP

////////////////////////////////////////////////////////////////////////////////
// Includes                                                                   //
////////////////////////////////////////////////////////////////////////////////

#include <string>
using std::string;

#include <vector>
using std::vector;

#include <boost/operators.hpp>
using boost::equality_comparable;

#include <boost/format.hpp>
using boost::format;

#include "common/ptr.hpp"
#include "common/types.hpp"

////////////////////////////////////////////////////////////////////////////////
// Associativity Type                                                         //
////////////////////////////////////////////////////////////////////////////////

/// Describes which of two operators of the same precedence binds more tightly.
///
enum Associativity {
	  NON_ASSOCIATIVE, ///< there can not be a conflict between two of the same operators
	 LEFT_ASSOCIATIVE, ///< the left-most operator binds more tightly
	RIGHT_ASSOCIATIVE, ///< the right-most operator binds more tightly
	CHAIN_ASSOCIATIVE, ///< neither binds more tightly; parentheses don't preserve meaning
	      ASSOCIATIVE  ///< neither binds more tightly; parentheses preserve meaning
};

////////////////////////////////////////////////////////////////////////////////
// Forward Declarations                                                       //
////////////////////////////////////////////////////////////////////////////////

class SingleOperator;
class CompositeOperator;
class Reference;

////////////////////////////////////////////////////////////////////////////////
// Operator Class                                                             //
////////////////////////////////////////////////////////////////////////////////

/// An operator in Mist.
///
class Operator : equality_comparable<Operator> {
	public:
		
		virtual ~Operator() {}
		
		/// \return true , if \a this binds more tightly than \a other
		///         false, otherwise
		///
		bool binds_tighter_than(sptr<Operator> other) const;
		
		/// \return true , if \a this is the same operator as \a other
		///         false, otherwise
		///
		virtual bool operator==(const Operator& other) const =0;
		
		/// \return the associativity of this operator
		///
		virtual Associativity associativity() const =0;
		
		/// \return the number of operands the operator takes
		///
		virtual ushort operand_count() const =0;
		
		/// \return the number of spaces that should be printed between operand and operator
		///
		virtual ushort spaces() const =0;
		
		/// \return a string-vector with, for each element:
		///         * if it is empty, it is a place for an operand
		///         * otherwise, it is an operator
		///
		virtual vector<string> echo() const =0;
	
	protected:
		
		virtual string get_format() const =0;
		
		virtual ushort precedence() const =0;
};

////////////////////////////////////////////////////////////////////////////////
// SingleOperator Class                                                       //
////////////////////////////////////////////////////////////////////////////////

/// An operator in Mist that is not a composite of multiple operators.
///
class SingleOperator : public Operator {
	public:
		
		/// Construct the operator with format \a format.
		///
		SingleOperator(const string& format);
		
		bool operator==(const Operator& other) const;
		
		string function_name() const;
		
		Associativity associativity() const;
		
		ushort operand_count() const;
		
		ushort spaces() const;
		
		vector<string> echo() const;
	
	private:
		
		string get_format() const;
		
		ushort precedence() const;
		
		static vector<string> parseOperator(const string& f);
		
		vector<string> _info;
		string         _functionName;
		short          _precedence;
		Associativity  _associativity;
		ushort         _operandCount;
		ushort         _spaces;
		
		friend class CompositeOperator;
};

////////////////////////////////////////////////////////////////////////////////
// CompositeOperator Class                                                    //
////////////////////////////////////////////////////////////////////////////////

/// An operator which is a composite of multiple operators.
///
class CompositeOperator : public Operator {
	public:
		
		/// Construct a composite operator with \a op as the outer operator.
		///
		CompositeOperator(sptr<SingleOperator> op);
		
		bool operator==(const Operator& other) const;
		
		Associativity associativity() const;
		
		ushort operand_count() const;
		
		ushort spaces() const;
		
		vector<string> echo() const;
		
		/// Add the operator \a op to this operator composition.
		///
		void add(sptr<SingleOperator> op);
		
		vector<wptr<SingleOperator> > operators() const;
	
	private:
		
		string get_format() const;
		
		ushort precedence() const;
		
		vector<sptr<SingleOperator> > _operators;
		
		friend class SingleOperator;
};

#endif // OPERATOR_HPP
